    /*  
    championBuilder - Champions Online character planner.
    Copyright (C) 2009 Moritz Hartmeier
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You can find the GNU General Public License in the data folder.
    */


package ch.zarzu.champions.builder.data;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import ch.zarzu.champions.builder.SystemLink;
import ch.zarzu.champions.builder.gui.PowerListItem;
import ch.zarzu.champions.builder.logic.BuildUpdater;
import ch.zarzu.util.AppPath;

public class PowerData {
    private BuildUpdater updater;
    private SystemLink sys_link;
    private boolean is_used, is_available;
    private Node xml;
    private NamedNodeMap attributes;
    private String app_path;
    private LinkedList<LinkedList<HashMap<String, String>>> effects = null;
    private LinkedList<LinkedList<String>> descriptions = null;
    private String name, id, type, item_type;
    private HashSet<PowerListItem> gui_elements;
    private HashSet<String> pools;

    public PowerData(Node xml_power) {
        xml = xml_power;
        updater = BuildUpdater.getInstance();
        sys_link = SystemLink.getInstance();
        
        app_path = AppPath.get();
        
        item_type = xml.getNodeName();
        attributes = xml_power.getAttributes();
        
        id = attributes.getNamedItem("id").getNodeValue();
        name = attributes.getNamedItem("name").getNodeValue();
        
        gui_elements = new HashSet<PowerListItem>();
        pools = new HashSet<String>();
        
        setUsed(false);
        setAvailable(true);
    }
    
    /**
     * fetches the effects from the xml and puts them into lists
     **/
    public LinkedList<LinkedList<HashMap<String, String>>> getEffects() {
        //use caching after first use
        if(effects == null) {
            effects = new LinkedList<LinkedList<HashMap<String, String>>>();
            LinkedList<HashMap<String, String>> effect;
            
            NamedNodeMap attrs;
            Node effect_node, node;
            NodeList effect_children, children = sys_link.getEffectById(attributes.getNamedItem("effect").getNodeValue());
            HashMap<String, String> map;
            for(int i = 1; i < children.getLength(); i += 2) {
                effect = new LinkedList<HashMap<String, String>>();
                if(children.item(i).getNodeName().equals("effect")) {
                    effect_node = children.item(i);
                    effect_children = effect_node.getChildNodes();
                    
                    attrs = effect_node.getAttributes();
                    
                    //add head
                    map = new HashMap<String, String>();
                    map.put("tag", "head");
                    for(int k = 0; k < attrs.getLength(); k++) {
                        map.put(attrs.item(k).getNodeName(), attrs.item(k).getNodeValue());
                    }
                    if(!map.containsKey("name") && !isAdvantage())
                        map.put("name", getName());
                    effect.add(map);
                    
                    //add all effect elements
                    for(int j = 1; j < effect_children.getLength(); j += 2) {
                        node = effect_children.item(j);
                        attrs = node.getAttributes();
                        
                        map = new HashMap<String, String>();
                        map.put("tag", node.getNodeName());
                        for(int k = 0; k < attrs.getLength(); k++) {
                            map.put(attrs.item(k).getNodeName(), attrs.item(k).getNodeValue());
                        }
                        effect.add(map);
                    }
                    effects.add(effect);
                }
            }
        }
        return effects;
    }
    
    public LinkedList<LinkedList<String>> getDescriptions() {
        //use caching after first use
        if(descriptions == null) {
            descriptions = new LinkedList<LinkedList<String>>();
            LinkedList<String> description;
            
            String[] plain_description_tags = {"General", "Tags"};
            Node node;
            NodeList description_children, children = xml.getChildNodes();
            
            description = new LinkedList<String>();
            description.add("Name");
            description.add(getName());
            descriptions.add(description);
            
            for(int i = 1; i < children.getLength(); i += 2) {
                description = new LinkedList<String>();
                
                if(children.item(i).getNodeName().equals("description")) {
                    node = children.item(i);
                    
                    description.add(node.getAttributes().getNamedItem("type").getNodeValue());
                    
                    if(Arrays.asList(plain_description_tags).contains(node.getAttributes().getNamedItem("type").getNodeValue()))
                        description.add(node.getTextContent().trim());
                    else {
                        description_children = children.item(i).getChildNodes();
                        for(int j = 1; j < description_children.getLength(); j += 2) {
                            node = description_children.item(j);
                            
                            if(node.getNodeName().equals("plus"))
                                description.add("<b>+</b> " + node.getTextContent().trim());
                            else
                                description.add("<b>-</b> " + node.getTextContent().trim());
                        }
                    }
                    descriptions.add(description);
                }
            }
        }
        return descriptions;
    }
    
    /**
     * searches all descriptions
     */
    public boolean plainTextSearch(String search) {
        String lower_search = search.toLowerCase();
        
        NodeList children = getNode().getChildNodes(), grand_children;
        if(name.toLowerCase().contains(lower_search))
            return true;
        for(int i = 0; i < children.getLength(); i++) {
            if(children.item(i).getTextContent().trim().toLowerCase().contains(lower_search))
                return true;
            
            grand_children = children.item(i).getChildNodes();
            for(int j = 0; j < grand_children.getLength(); j++) {
                if(grand_children.item(j).getTextContent().trim().toLowerCase().contains(lower_search))
                    return true;
            }
        }
        return false;
    }
    
    /**
     * searches all effects for procs/modes
     */
    public boolean effectSearch(String search) {
        String lower_search = search.toLowerCase();
        
        for(LinkedList<HashMap<String, String>> list : getEffects()) {
            for(HashMap<String, String> effect : list) {
                if(effect.get("tag").equals("proc") || effect.get("tag").equals("mode")){
                    if(sys_link.translate(effect.get("name")).toLowerCase().contains(lower_search))
                        return true;
                } else if(effect.get("tag").equals("reactive")) {
                    if(sys_link.translate(effect.get("reaction")).toLowerCase().contains(lower_search))
                        return true;
                }
            }
        }
        return false;
    }
    
    /**
     * searches all effects for damage of the given type
     */
    public boolean damageTypeSearch(String search) {
        String lower_search = search.toLowerCase();
        
        for(LinkedList<HashMap<String, String>> list : getEffects()) {
            for(HashMap<String, String> effect : list) {
                if(!effect.get("tag").equals("head") && effect.containsKey("type") && !effect.get("tag").equals("cc"))
                    if(sys_link.translate(effect.get("type") + "_dmg").toLowerCase().contains(lower_search))
                        return true;
            }
        }
        return false;
    }
    
    /**
     * searches all effects for damage of the given type
     */
    public boolean buffSearch(String search) {
        String lower_search = search.toLowerCase();
        
        for(LinkedList<HashMap<String, String>> list : getEffects()) {
            for(HashMap<String, String> effect : list) {
                if(!effect.get("tag").equals("head") && effect.containsKey("char"))
                    if(sys_link.translate(effect.get("char")).toLowerCase().contains(lower_search))
                        return true;
            }
        }
        return false;
    }
    
    public void setType(String power_type) {
        type = power_type;
    }
    
    public String getType() {
        return type;
    }
    
    public void addToPool(String pool_id) {
        pools.add(pool_id);
    }
    
    public boolean belongsToPool(String pool_id) {
        return pools.contains(pool_id);
    }
    
    public String getId() {
        return id;
    }
    
    public String getTier() {
        try {
            return attributes.getNamedItem("tier").getNodeValue();
        } catch(NullPointerException e) {
            return "";
        }
    }
    
    //for advantages which don't have the full id in their xml
    public void setId(String id_string) {
        id = id_string;
    }
    
    public String getName() {
        return name;
    }
    
    public int getCost() {
        return Integer.parseInt(attributes.getNamedItem("cost").getNodeValue());
    }
    
    //returns the cost of this advantage
    public String getAdds() {
        try {
            return attributes.getNamedItem("adds").getNodeValue();
        } catch(NullPointerException e) {
            return "";
        }
    }
    
    public String getRequirements() {
        try {
            return attributes.getNamedItem("requires").getNodeValue();
        } catch (NullPointerException e) {
            return "";
        }
    }
    
    public String getInnate() {
        try {
            return attributes.getNamedItem("innate").getNodeValue();
        } catch (NullPointerException e) {
            return "";
        }
    }
    
    public String getEnergyBuilder() {
        try {
            return attributes.getNamedItem("energy").getNodeValue();
        } catch (NullPointerException e) {
            return "";
        }
    }
    
    public String getTierZeroPower() {
        try {
            return attributes.getNamedItem("power").getNodeValue();
        } catch (NullPointerException e) {
            return "";
        }
    }
    
    public String getDefaultPool() {
        try {
            return attributes.getNamedItem("pool").getNodeValue();
        } catch (NullPointerException e) {
            return "";
        }
    }
    
    public Node getNode() {
        return xml;
    }
    
    public void setUsed(boolean used) {
        for(PowerListItem e : gui_elements) {
            e.setUsed(used);
        }
        is_used = used;
    }
    
    public void setAvailable(boolean available) {
        if(available) {
            setUsed(isUsed());
        } else {
            for(PowerListItem e : gui_elements) {
                e.setUnavailable();
            }
        }
        is_available = available;
    }
    
    public void subscribe(PowerListItem item) {
        gui_elements.add(item);
        item.setName(getName());
        if(isAdvantage())
            item.setCost(Integer.toString(getCost()));
    }
    
    /**
     * sets the visibility of the lock
     */
    public void setLockVisible(boolean b) {
        for(PowerListItem e : gui_elements) {
            e.setLockVisible(b);
        }
    }
    
    public String getPath() {
        return app_path + attributes.getNamedItem("path").getNodeValue();
    }
    
    public boolean isUsed() {
        return is_used;
    }
    
    public boolean isAvailable() {
        return is_available;
    }
    
    public boolean isAdvantage() {
        return item_type.equals("advantage");
    }
    
    public boolean isPower() {
        return item_type.equals("power") || item_type.equals("stat") || item_type.equals("talent");
    }
    
    public boolean isFramework() {
        return item_type.equals("framework");
    }
    
    /**
     * update anything that contains translatable text
     */
    public void updateLanguage(Node xml_power) {
        for(PowerListItem item : gui_elements) {
            xml = xml_power;
            
            attributes = xml_power.getAttributes();
            name = attributes.getNamedItem("name").getNodeValue();
            item.setName(name);
            
            //force effects to be reloaded on next use
            effects = null;
        }
    }
}